import axios from 'axios';
import * as crypto from 'crypto';
import { WxConfig } from './types';

/**
 * 微信支付SDK
 */
export class WxPaySDK {
  private appId: string;
  private appSecret: string;
  private mchId: string;        // 商户号
  private mchKey: string;       // 商户API密钥
  private notifyUrl: string;    // 支付结果通知地址
  private baseUrl = 'https://api.mch.weixin.qq.com';

  constructor(config: {
    appId: string;
    appSecret: string;
    mchId: string;
    mchKey: string;
    notifyUrl: string;
  }) {
    this.appId = config.appId;
    this.appSecret = config.appSecret;
    this.mchId = config.mchId;
    this.mchKey = config.mchKey;
    this.notifyUrl = config.notifyUrl;
  }

  /**
   * 生成随机字符串
   */
  private generateNonceStr(length = 32): string {
    const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    let nonceStr = '';
    for (let i = 0; i < length; i++) {
      nonceStr += chars.charAt(Math.floor(Math.random() * chars.length));
    }
    return nonceStr;
  }

  /**
   * 生成签名
   * @param params 参数对象
   */
  public generateSign(params: Record<string, any>): string {
    // 1. 按字典序排序参数
    const keys = Object.keys(params).sort();
    
    // 2. 组装签名字符串
    let signStr = '';
    for (const key of keys) {
      if (key !== 'sign' && params[key] !== undefined && params[key] !== '') {
        signStr += `${key}=${params[key]}&`;
      }
    }
    
    // 3. 加入API密钥
    signStr += `key=${this.mchKey}`;
    
    // 4. 使用MD5加密并转为大写
    const crypto = require('crypto');
    const sign = crypto.createHash('md5').update(signStr).digest('hex').toUpperCase();
    
    return sign;
  }

  /**
   * 将对象转为XML字符串
   * @param obj 对象
   */
  private objectToXml(obj: Record<string, any>): string {
    let xml = '<xml>';
    for (const key in obj) {
      if (obj[key] !== undefined && obj[key] !== null && obj[key] !== '') {
        xml += `<${key}>${obj[key]}</${key}>`;
      }
    }
    xml += '</xml>';
    return xml;
  }

  /**
   * 将XML字符串转为对象
   * @param xml XML字符串
   */
  private xmlToObject(xml: string): Record<string, any> {
    const result: Record<string, any> = {};
    
    // 记录原始XML以便调试
    console.log('开始解析XML:', xml.substring(0, 300) + (xml.length > 300 ? '...' : ''));
    
    if (!xml || xml.trim() === '') {
      console.error('XML为空，无法解析');
      return result;
    }
    
    try {
      // 检查是否是有效的XML
      if (!xml.includes('<xml>') || !xml.includes('</xml>')) {
        console.error('无效的XML格式，缺少<xml>标签');
        return result;
      }
      
      // 提取xml标签内的内容
      const xmlContent = xml.match(/<xml>([\s\S]*?)<\/xml>/);
      if (!xmlContent || !xmlContent[1]) {
        console.error('无法提取<xml>标签内容');
        return result;
      }
      
      // 使用更强大的方法解析XML内容
      const content = xmlContent[1];
      const tagRegex = /<([\w_]+)>([\s\S]*?)<\/\1>|<([\w_]+)\s*\/>/g;
      let match;
      
      while ((match = tagRegex.exec(content)) !== null) {
        const key = match[1] || match[3];
        let value = match[2] || '';
        
        if (key) {
          // 处理CDATA标签
          const cdataMatch = value.match(/<!\[CDATA\[([\s\S]*?)\]\]>/);
          if (cdataMatch) {
            value = cdataMatch[1];
            console.log(`提取CDATA内容 ${key}: ${value}`);
          }
          
          // 特殊处理 transaction_id 和 time_end，保持为字符串
          if (key === 'transaction_id' || key === 'time_end') {
            result[key] = value;
            continue;
          }
          
          // 尝试解析数字
          if (/^\d+$/.test(value)) {
            result[key] = parseInt(value, 10);
          } else if (/^\d+\.\d+$/.test(value)) {
            result[key] = parseFloat(value);
          } else {
            result[key] = value;
          }
        }
      }
      
      console.log('XML解析结果(处理CDATA后):', result);
      
      return result;
    } catch (error: any) {
      console.error('XML解析失败:', error.message);
      return result;
    }
  }

  /**
   * 统一下单
   * @param params 下单参数
   */
  async unifiedOrder(params: {
    openid: string;           // 用户openid
    outTradeNo: string;       // 商户订单号
    totalFee: number;         // 订单总金额，单位为分
    body: string;             // 商品描述
    tradeType?: string;       // 交易类型，默认为JSAPI
    attach?: string;          // 附加数据
    timeExpire?: string;      // 订单失效时间
  }): Promise<any> {
    const url = `${this.baseUrl}/pay/unifiedorder`;
    
    // 参数校验
    if (!params.openid) {
      console.error('统一下单失败: openid 为空');
      return {
        success: false,
        errorCode: 'PARAM_ERROR',
        errorMessage: 'openid 不能为空'
      };
    }
    
    if (!params.outTradeNo) {
      console.error('统一下单失败: 商户订单号为空');
      return {
        success: false,
        errorCode: 'PARAM_ERROR',
        errorMessage: '商户订单号不能为空'
      };
    }
    
    if (!params.totalFee || params.totalFee <= 0) {
      console.error('统一下单失败: 订单金额无效', params.totalFee);
      return {
        success: false,
        errorCode: 'PARAM_ERROR',
        errorMessage: '订单金额必须大于0'
      };
    }
    
    if (!params.body) {
      console.error('统一下单失败: 商品描述为空');
      return {
        success: false,
        errorCode: 'PARAM_ERROR',
        errorMessage: '商品描述不能为空'
      };
    }
    
    const requestData: Record<string, any> = {
      appid: this.appId,
      mch_id: this.mchId,
      nonce_str: this.generateNonceStr(),
      body: params.body,
      out_trade_no: params.outTradeNo,
      total_fee: params.totalFee,
      spbill_create_ip: '127.0.0.1',  // 终端IP，这里固定为本机IP
      notify_url: this.notifyUrl,
      trade_type: params.tradeType || 'JSAPI',
      openid: params.openid,
      attach: params.attach || '',
      time_expire: params.timeExpire || ''
    };
    
    // 生成签名
    const sign = this.generateSign(requestData);
    requestData['sign'] = sign;
    
    // 将对象转为XML
    const xmlData = this.objectToXml(requestData);
    
    console.log('统一下单请求数据:', {
      url,
      requestData: { ...requestData, sign: '***' } // 隐藏签名
    });
    
    try {
      console.log('开始发送统一下单请求...');
      console.log(`请求XML数据: ${xmlData}`);
      
      const response = await axios.post(url, xmlData, {
        headers: { 'Content-Type': 'text/xml' },
        timeout: 10000 // 设置10秒超时
      });
      
      console.log('统一下单请求成功，开始解析响应...');
      console.log(`响应数据: ${typeof response.data === 'string' ? response.data.substring(0, 300) : JSON.stringify(response.data).substring(0, 300)}`);
      
      // 将XML响应转为对象
      const responseData = this.xmlToObject(response.data);
      
      console.log('统一下单响应数据(解析后):', {
        return_code: responseData.return_code,
        return_msg: responseData.return_msg,
        result_code: responseData.result_code,
        err_code: responseData.err_code,
        err_code_des: responseData.err_code_des,
        prepay_id: responseData.prepay_id
      });
      
      if (responseData.return_code === 'SUCCESS' && responseData.result_code === 'SUCCESS') {
        // 返回小程序支付需要的参数
        const timeStamp = Math.floor(Date.now() / 1000).toString();
        const nonceStr = this.generateNonceStr();
        
        const payParams: Record<string, any> = {
          appId: this.appId,
          timeStamp,
          nonceStr,
          package: `prepay_id=${responseData.prepay_id}`,
          signType: 'MD5'
        };
        
        // 生成签名
        const paySign = this.generateSign(payParams);
        
        return {
          success: true,
          data: {
            timeStamp,
            nonceStr,
            package: payParams.package,
            signType: payParams.signType,
            paySign,
            orderId: params.outTradeNo,
            prepayId: responseData.prepay_id
          }
        };
      } else {
        console.error('统一下单失败，微信返回错误:', {
          return_code: responseData.return_code,
          return_msg: responseData.return_msg,
          result_code: responseData.result_code || '',
          err_code: responseData.err_code || '',
          err_code_des: responseData.err_code_des || ''
        });
        
        return {
          success: false,
          errorCode: responseData.err_code || responseData.return_code,
          errorMessage: responseData.err_code_des || responseData.return_msg,
          rawResponse: typeof response.data === 'string' ? response.data.substring(0, 1000) : JSON.stringify(response.data).substring(0, 1000)
        };
      }
    } catch (error: any) {
      console.error('统一下单请求失败:', {
        message: error.message,
        stack: error.stack,
        url,
        params: { ...params, openid: '***' } // 隐藏敏感信息
      });
      
      // 检查是否是网络错误
      let errorCode = 'SYSTEM_ERROR';
      let errorMessage = error.message || '系统错误';
      
      if (error.code === 'ECONNABORTED') {
        errorCode = 'TIMEOUT';
        errorMessage = '请求超时，请稍后重试';
      } else if (error.code === 'ECONNREFUSED') {
        errorCode = 'CONNECTION_REFUSED';
        errorMessage = '连接被拒绝，请检查网络设置';
      } else if (error.response) {
        // 服务器返回了错误状态码
        errorCode = `HTTP_ERROR_${error.response.status}`;
        errorMessage = `HTTP错误: ${error.response.status}`;
      }
      
      return {
        success: false,
        errorCode: errorCode,
        errorMessage: errorMessage
      };
    }
  }

  /**
   * 查询订单
   * @param outTradeNo 商户订单号
   */
  async queryOrder(outTradeNo: string): Promise<any> {
    const url = `${this.baseUrl}/pay/orderquery`;
    
    const requestData: Record<string, any> = {
      appid: this.appId,
      mch_id: this.mchId,
      out_trade_no: outTradeNo,
      nonce_str: this.generateNonceStr()
    };
    
    // 生成签名
    const sign = this.generateSign(requestData);
    requestData['sign'] = sign;
    
    // 将对象转为XML
    const xmlData = this.objectToXml(requestData);
    
    console.log(`开始查询订单 ${outTradeNo}，请求数据:`, {
      url,
      requestData: { ...requestData, sign: '***' } // 隐藏签名
    });
    
    try {
      console.log(`发送订单查询请求: ${outTradeNo}`);
      console.log(`请求XML数据: ${xmlData}`);
      
      const response = await axios.post(url, xmlData, {
        headers: { 'Content-Type': 'text/xml' },
        timeout: 10000 // 设置10秒超时
      });
      
      console.log(`订单查询响应成功: ${outTradeNo}，状态码: ${response.status}`);
      console.log(`响应数据: ${typeof response.data === 'string' ? response.data.substring(0, 300) : JSON.stringify(response.data).substring(0, 300)}`);
      
      // 检查响应是否为空
      if (!response.data) {
        console.error(`订单查询响应数据为空: ${outTradeNo}`);
        return {
          success: false,
          errorCode: 'EMPTY_RESPONSE',
          errorMessage: '微信支付返回空响应'
        };
      }
      
      // 将XML响应转为对象
      const responseData = this.xmlToObject(response.data);
      
      // 检查解析结果是否为空对象
      if (Object.keys(responseData).length === 0) {
        console.error(`订单 ${outTradeNo} 响应数据解析失败，原始响应:`, 
          typeof response.data === 'string' ? response.data.substring(0, 500) : JSON.stringify(response.data).substring(0, 500));
        
        return {
          success: false,
          errorCode: 'PARSE_ERROR',
          errorMessage: '解析微信支付响应失败',
          rawResponse: typeof response.data === 'string' ? response.data.substring(0, 1000) : JSON.stringify(response.data).substring(0, 1000)
        };
      }
      
      console.log(`订单 ${outTradeNo} 查询响应数据(解析后):`, responseData);
      
      if (responseData.return_code === 'SUCCESS' && responseData.result_code === 'SUCCESS') {
        // 订单查询成功，返回订单状态
        // 确保时间格式正确
        let timeEnd = responseData.time_end;
        if (timeEnd) {
          // 确保 timeEnd 是字符串类型
          const timeEndStr = timeEnd.toString();
          console.log(`原始支付时间: ${timeEndStr}, 类型: ${typeof timeEnd}`);
          timeEnd = timeEndStr;
        }
        
        // 确保 transaction_id 是字符串类型
        const transactionId = responseData.transaction_id ? responseData.transaction_id.toString() : '';
        console.log(`交易ID: ${transactionId}, 类型: ${typeof transactionId}`);
        
        return {
          success: true,
          data: {
            tradeState: responseData.trade_state,
            tradeStateDesc: responseData.trade_state_desc,
            outTradeNo: responseData.out_trade_no,
            transactionId: transactionId,
            openid: responseData.openid,
            totalFee: parseInt(responseData.total_fee || '0'),
            timeEnd: timeEnd ? timeEnd.toString() : ''
          }
        };
      } else {
        console.error(`订单 ${outTradeNo} 查询失败，微信返回错误:`, {
          return_code: responseData.return_code || '未返回',
          return_msg: responseData.return_msg || '未返回',
          result_code: responseData.result_code || '未返回',
          err_code: responseData.err_code || '未返回',
          err_code_des: responseData.err_code_des || '未返回'
        });
        
        return {
          success: false,
          errorCode: responseData.err_code || responseData.return_code || 'UNKNOWN_ERROR',
          errorMessage: responseData.err_code_des || responseData.return_msg || '微信支付查询接口返回失败',
          rawResponse: typeof response.data === 'string' ? response.data.substring(0, 1000) : JSON.stringify(response.data).substring(0, 1000)
        };
      }
    } catch (error: any) {
      console.error(`查询订单 ${outTradeNo} 失败:`, {
        message: error.message,
        stack: error.stack,
        code: error.code,
        url,
        requestData: { ...requestData, sign: '***' } // 隐藏签名
      });
      
      // 检查是否是网络错误
      let errorCode = 'SYSTEM_ERROR';
      let errorMessage = error.message || '系统错误';
      
      if (error.code === 'ECONNABORTED') {
        errorCode = 'TIMEOUT';
        errorMessage = '请求超时，请稍后重试';
      } else if (error.code === 'ECONNREFUSED') {
        errorCode = 'CONNECTION_REFUSED';
        errorMessage = '连接被拒绝，请检查网络设置';
      } else if (error.response) {
        // 服务器返回了错误状态码
        errorCode = `HTTP_ERROR_${error.response.status}`;
        errorMessage = `HTTP错误: ${error.response.status}`;
      }
      
      return {
        success: false,
        errorCode: errorCode,
        errorMessage: errorMessage
      };
    }
  }

  /**
   * 处理支付结果通知
   * @param xmlData 微信支付结果通知的XML数据
   */
  parsePayNotify(xmlData: any): any {
    try {
      // 确保xmlData是字符串类型
      let xmlString = '';
      if (typeof xmlData === 'string') {
        xmlString = xmlData;
      } else if (xmlData && typeof xmlData === 'object') {
        // 如果是Buffer或其他对象，尝试转换为字符串
        if (Buffer.isBuffer(xmlData)) {
          xmlString = xmlData.toString('utf8');
        } else if (xmlData.toString) {
          xmlString = xmlData.toString();
        }
      }
      
      console.log('开始解析支付通知数据类型:', typeof xmlData);
      if (xmlString) {
        console.log('支付通知数据预览:', xmlString.substring(0, 300) + (xmlString.length > 300 ? '...' : ''));
      } else {
        console.error('无法将支付通知数据转换为字符串');
      }
      
      if (!xmlString || xmlString.trim() === '') {
        console.error('支付通知数据为空');
        return {
          success: false,
          errorCode: 'EMPTY_DATA',
          errorMessage: '支付通知数据为空'
        };
      }
      
      // 将XML转为对象
      const notifyData = this.xmlToObject(xmlString);
      
      console.log('支付通知数据解析结果:', notifyData);
      
      // 检查解析结果是否为空对象
      if (Object.keys(notifyData).length === 0) {
        console.error('支付通知数据解析失败，原始数据:', xmlString.substring(0, 500));
        return {
          success: false,
          errorCode: 'PARSE_ERROR',
          errorMessage: '解析支付通知失败'
        };
      }
      
      // 验证签名
      const sign = notifyData.sign;
      if (!sign) {
        console.error('支付通知数据缺少签名');
        return {
          success: false,
          errorCode: 'MISSING_SIGN',
          errorMessage: '支付通知数据缺少签名'
        };
      }
      
      delete notifyData.sign;
      const calculatedSign = this.generateSign(notifyData);
      
      if (calculatedSign !== sign) {
        console.error('支付通知签名验证失败:', {
          receivedSign: sign,
          calculatedSign: calculatedSign
        });
        return {
          success: false,
          errorCode: 'SIGN_ERROR',
          errorMessage: '签名验证失败'
        };
      }
      
      if (notifyData.return_code === 'SUCCESS' && notifyData.result_code === 'SUCCESS') {
        // 确保 transaction_id 是字符串类型
        const transactionId = notifyData.transaction_id ? notifyData.transaction_id.toString() : '';
        console.log(`通知中的交易ID: ${transactionId}, 类型: ${typeof transactionId}`);
        
        return {
          success: true,
          data: {
            outTradeNo: notifyData.out_trade_no,
            transactionId: transactionId,
            openid: notifyData.openid,
            totalFee: parseInt(notifyData.total_fee || '0'),
            timeEnd: notifyData.time_end,
            attach: notifyData.attach || ''
          }
        };
      } else {
        console.error('支付通知显示支付失败:', {
          return_code: notifyData.return_code,
          return_msg: notifyData.return_msg,
          result_code: notifyData.result_code || '',
          err_code: notifyData.err_code || '',
          err_code_des: notifyData.err_code_des || ''
        });
        
        return {
          success: false,
          errorCode: notifyData.err_code || notifyData.return_code,
          errorMessage: notifyData.err_code_des || notifyData.return_msg
        };
      }
    } catch (error: any) {
      console.error('解析支付通知失败:', {
        message: error.message,
        stack: error.stack,
        xmlDataType: typeof xmlData,
        xmlDataPreview: typeof xmlData === 'string' ? xmlData.substring(0, 200) : '非字符串类型'
      });
      
      return {
        success: false,
        errorCode: 'PARSE_ERROR',
        errorMessage: error.message || '解析失败'
      };
    }
  }

  /**
   * 生成支付结果通知的响应
   * @param success 是否成功
   */
  generateNotifyResponse(success: boolean): string {
    if (success) {
      return this.objectToXml({
        return_code: 'SUCCESS',
        return_msg: 'OK'
      });
    } else {
      return this.objectToXml({
        return_code: 'FAIL',
        return_msg: 'FAIL'
      });
    }
  }
}

/**
 * 创建微信支付SDK实例
 * @param config 配置
 */
export function createWxPaySDK(config: {
  appId: string;
  appSecret: string;
  mchId: string;
  mchKey: string;
  notifyUrl: string;
}): WxPaySDK {
  return new WxPaySDK(config);
} 